import Config, {configChange} from '../Config'
import {message} from 'antd'
import {isString} from "../util";
import download from "downloadjs"
import {getIntl, isDevelopment, log} from "../util/AppUtil";
import LandService from "./LandService";
import LoginService from "./LoginService";
import LocalDataService from "./LocalDataService";
import {addUrlParams} from "../util/URLUtil";



// 项目前缀
let prefix;
configChange((Config) => {
    prefix = Config.api.prefix;
});



// get请求缓存对象
let cacheObj = {};

/**
 * 发起http请求
 * @param url 请求地址
 * @param init 参数对象， 详情请查看fetch的参数
 * @param errorMsg 错误提示 默认打开
 * @returns {Promise<any>}
 */
async function http(url, init = {}, errorMsg = true) {
    let headers = new Headers(),
        showErrorMessage = (msg) => {
            if(errorMsg){
                message.error(msg)
            }
        };
    if(!init.body || isString(init.body)){
        headers.append("Content-Type", "application/json");
    }
    headers.append("language", LocalDataService.getItem(LandService.LOCAL_LAND_KEY));
    headers.append("access-token", LocalDataService.getItem(LoginService.LOGIN_KEY));
    return new Promise(function(resolve, reject){
        fetch(http.getUrl(url), {method: "POST", cache: "no-cache", credentials: "include", headers,  ...init}).then(async function(res){
            if(res.ok){
                try{
                    let json = await res.json();
                    // 1:成功  -1: 部分成功
                    if(json.code === "1" || json.code === "-1") {
                        resolve(json);
                    }else if(json.code === "-2"){
                        // message.error(json.msg || "登录失效");
                        // 非登录状态
                        LoginService.logout();
                        reject(json);
                    }else{
                        showErrorMessage(json.msg || getIntl().formatMessage({id: "operatingError"}));
                        reject(json);
                    }
                }catch(e){
                    showErrorMessage(getIntl().formatMessage({id: "httpJSONError"}));
                    reject(e);
                }

            }else{
                // 请求失败,状态码 {status}
                showErrorMessage(getIntl().formatMessage({id: "httpFailStatus"}, {status: res.status}));
                reject(res);
            }
        }, function(err){
            showErrorMessage(getIntl().formatMessage({id: "httpFail"}));
            reject(err);
        })
    });
}

/**
 * 后端返回json处理
 * @param json json对象
 * @param resolve promise对应方法
 * @param reject promise对应方法
 */
function resultJsonCheck(json, resolve, reject){
    // 1:成功  -1: 部分成功
    if(json.code === "1" || json.code === "-1"){
        resolve(json);
    }else if(json.code === "-2"){
        // message.error(json.msg || "登录失效");
        // 非登录状态
        LoginService.logout();
        reject(json);
    }else{
        message.error(json.msg || getIntl().formatMessage({id: "operatingError"}));
        reject(json);
    }
}


http.download = (url, progress) => {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest(),
            // 当url中有://协议说明是绝对地址 不需要加前缀
            targetUrl = http.getUrl(url);

        if(url.indexOf("://") !== -1){
            targetUrl = url;
            xhr.open("GET", targetUrl, true);
        }else{
            xhr.open("GET", targetUrl, true);
            // 只有本站加token
            xhr.setRequestHeader("language", LocalDataService.getItem(LandService.LOCAL_LAND_KEY));
            xhr.setRequestHeader("access-token", LocalDataService.getItem(LoginService.LOGIN_KEY));
        }
        xhr.responseType = "blob";
        xhr.addEventListener("progress", function(evt){
            progress && progress(evt);
        });
        xhr.onload = function(e){
            let blob = e.target.response;
            // 下载出错返回的是json数据
            if(blob.type === "application/json"){
                // 将Blob数据转换为文本=>再转为对象
                let reader = new FileReader();
                reader.onload = () => {
                    let json = JSON.parse(reader.result);
                    resultJsonCheck(json, resolve, reject)
                };
                reader.readAsText(blob);
                return;
            }
            // e.target.response.type  application/json
            let disposition = e.currentTarget.getResponseHeader("content-disposition"),
                filename = disposition.match(/filename=(.*)/)[1];
            download(e.target.response, decodeURIComponent(filename));
            resolve();
        };
        xhr.send();
    });

};

/**
 * 使用xhr发送请求,有进度事件, fetch暂时没有进度
 * @param url
 * @param init
 * @param onProgress 进度回调
 * @returns {Promise<any>}
 */
http.xhr = (url, init, onProgress) => {
    let headers = {};
    if(!init.body || isString(init.body)){
        headers["Content-Type"] = "application/json";
    }
    headers["language"] = LocalDataService.getItem(LandService.LOCAL_LAND_KEY);
    headers["access-token"] = LocalDataService.getItem(LoginService.LOGIN_KEY);
    return new Promise(function(resolve, reject){
        futch(http.getUrl(url), {method: "POST", cache: "no-cache", headers,  ...init}, onProgress).then(async function(resText){
            try{
                let json = JSON.parse(resText);
                // 1:成功  -1: 部分成功
                if(json.code === "1" || json.code === "-1"){
                    resolve(json);
                }else if(json.code === "-2"){
                    // message.error(json.msg || "登录失效");
                    // 非登录状态
                    LoginService.logout();
                    reject(json);
                }else{
                    // message.error(json.msg || "操作失败");
                    reject(json);
                }
            }catch(e){
                // message.error("返回JSON格式错误" + resText);
                reject(e);
            }

        }, function(err){
            // message.error(`请求失败`);
            reject(err);
        })
    });
};

/**
 * 将普通url加前缀
 * @param url
 * @return {*}
 */
http.getUrl = function(url) {
    if(!url){
        url = "";
    }
    // 需要加前缀
    if(url.substr(0, 1) === "/"){
        // 在开发环境且localStorage中有自定义的后台地址
        if(isDevelopment(false) && localStorage.backUrl){
            url = localStorage.backUrl + url;
        }else{
            url = prefix + url;
        }
    }
    return url;
};

function futch(url, opts = {}, onProgress) {
    return new Promise( (res, rej)=>{
        let xhr = new XMLHttpRequest();
        xhr.open(opts.method || 'get', url);
        for (let k in opts.headers||{})
            xhr.setRequestHeader(k, opts.headers[k]);
        xhr.onload = (e) => res(e.target.responseText);
        xhr.onerror = rej;
        if (xhr.upload && onProgress)
            xhr.upload.onprogress = function(e){
                onProgress.call(this, e, xhr);
            }; // event.loaded / event.total * 100 ; //event.lengthComputable
        xhr.send(opts.body);
    });
}

/**
 * 给url添加拼接参数
 * @param url
 * @param params 参数对象
 * @param isUrl 是否url，默认true, 为true不存在？则会自动添加？ false则不会添加
 * @returns {*} 添加参数之后的url
 */
http.addUrlParams = addUrlParams;

/**
 * 用GET方式请求, 自动增加随机参数, 防止IE缓存
 * @param url 请求的url地址
 * @param params 请求参数对象，会自动拼接在url后面
 * @param cache 是否缓存 默认false 内部实现的缓存机制
 * @returns {Promise<*>}
 */
http.get = async (url, params = {}, cache = false, errorMsg = true) => {
    let realUrl = http.addUrlParams(url, params);
    if(cache){
        let cacheResult = cacheObj[realUrl];
        if(cacheResult){
            log("use cache", url, params, cacheResult);
            return cacheResult;
        }
    }
    let result = await http(http.addUrlParams(realUrl, {_: Math.random()}), {method: "GET"}, errorMsg);
    if(cache && Config.cache){
        cacheObj[realUrl] = result;
    }
    return result;
};

/**
 * 用DELETE方式请求, 自动增加随机参数, 防止IE缓存
 * delete为ie关键字 所以方法名加一个R
 * @param url 请求的url地址
 * @param params 请求参数对象，会自动放在body中
 * @returns {Promise<*>}
 */
http.deleteR = async (url, params, errorMsg = true) => {
    return http(url, {method: "DELETE", body: isString(params) ? params : JSON.stringify(params)}, errorMsg)
};


http.cleanCache = () => {
    cacheObj = {};
};


export default http;

